package com.why.cachecloud.client.redisson.util;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.*;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Slf4j
public class RedisUtils {

    private final RedissonClient redissonClient;

    public RedisUtils(RedissonClient client) {
        this.redissonClient = client;
    }

    //----- string

    /**
     * 如果 key 已经存在并且是一个字符串， APPEND 命令将 value 追加到 key 原来的值的末尾。
     * <p>
     * 如果 key 不存在， APPEND 就简单地将给定 key 设为 value ，就像执行 SET key value 一样。
     * <p>
     * 注意默认是fastjson编码格式，如果获取append命令执行后的key，请指定 StringCodec codec。
     * <p>
     * 时间复杂度：O(1)
     *
     * @param key     键名
     * @param content 追加内容
     * @throws Exception 异常信息
     */
    public void append(String key, String content) throws Exception {
        if (StringUtils.isEmpty(content)) {
            throw new Exception("追加内容不合法！");
        }
        byte[] bytes = content.getBytes(StandardCharsets.UTF_8);
        redissonClient.getBinaryStream(key).getOutputStream().write(bytes);
    }

    /**
     * 将 key 中储存的数字值减一。
     * <p>
     * 如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 DECR 操作。
     * <p>
     * 如果值包含错误的类型，或字符串类型的值不能表示为数字，那么返回一个错误。
     *
     * @param key 键名
     */
    public long decrLong(String key) {
        return redissonClient.getAtomicLong(key).decrementAndGet();
    }

    /**
     * 将 key 中储存的数字值增一。
     * <p>
     * 如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 INCR 操作。
     * <p>
     * 如果值包含错误的类型，或字符串类型的值不能表示为数字，那么返回一个错误
     *
     * @param key 键名
     */
    public long incrLong(String key) {
        return redissonClient.getAtomicLong(key).incrementAndGet();
    }

    /**
     * 将key对应的value值减少/增加指定的整数
     * 正数为加，负数为减
     *
     * @param key    键名
     * @param number 整数 正数为加，负数为减
     */
    public long incrOrDecrByLong(String key, long number) {
        return redissonClient.getAtomicLong(key).addAndGet(number);
    }

    /**
     * 将 key 中储存的数字值减一。
     * <p>
     * 如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 DECR 操作。
     * <p>
     * 如果值包含错误的类型，或字符串类型的值不能表示为数字，那么返回一个错误。
     *
     * @param key 键名
     */
    public double decrDouble(String key) {
        return redissonClient.getAtomicDouble(key).decrementAndGet();
    }

    /**
     * 将 key 中储存的数字值增一。
     * <p>
     * 如果 key 不存在，那么 key 的值会先被初始化为 0 ，然后再执行 INCR 操作。
     * <p>
     * 如果值包含错误的类型，或字符串类型的值不能表示为数字，那么返回一个错误
     *
     * @param key 键名
     */
    public double incrDouble(String key) {
        return redissonClient.getAtomicDouble(key).incrementAndGet();
    }

    /**
     * 将key对应的value值减少/增加指定的小数
     * 正数为加，负数为减
     *
     * @param key    键名
     * @param number 小数 正数为加，负数为减
     */
    public double incrOrDecrByDouble(String key, double number) {
        return redissonClient.getAtomicDouble(key).addAndGet(number);
    }

    /**
     * 返回 key 所关联的字符串值。
     * <p>
     * 如果 key 不存在那么返回特殊值 null 。
     * <p>
     * 假如 key 储存的值不是字符串类型，返回一个错误，因为 GET 只能用于处理字符串值。
     *
     * @param <V> 键值的类型
     * @param key 键名
     * @return 键值
     */
    public <V> V get(String key) {
        RBucket<V> bucket = redissonClient.getBucket(key);
        return bucket.get();
    }

    /**
     * 将给定 key 的值设为 value ，并返回 key 的旧值(old value)。
     * <p>
     * 当 key 存在但不是字符串类型时，返回一个错误。
     *
     * @param key      名称
     * @param newValue 新值
     * @param <V>      newValue 类型
     * @return 返回给定 key 的旧值。当 key 没有旧值时，也就是 key 不存在时，返回 null 。
     */
    public <V> V getSet(String key, V newValue) {
        RBucket<V> bucket = redissonClient.getBucket(key);
        return bucket.getAndSet(newValue);
    }

    /**
     * 返回所有(一个或多个)给定 key 的值。
     * <p>
     * 如果给定的 key 里面，有某个 key 不存在，那么这个 key 返回特殊值 null 。因此，该命令永不失败。
     * cluster模式无法使用
     *
     * @param keys 多个键名
     * @param <V>  键值的类型
     * @return 一个包含所有给定 key-value 的值的map
     */
    public <V> Map<String, V> mget(String... keys) {
        RBuckets buckets = redissonClient.getBuckets();
        return buckets.get(keys);
    }

    /**
     * 同时设置一个或多个 key-value 对。
     * <p>
     * 如果某个给定 key 已经存在，那么 MSET 会用新值覆盖原来的旧值，如果这不是你所希望的效果，请考虑使用 MSETNX 命令：它只会在所有给定 key 都不存在的情况下进行设置操作。
     * <p>
     * MSET 是一个原子性(atomic)操作，所有给定 key 都会在同一时间内被设置，某些给定 key 被更新而另一些给定 key 没有改变的情况，不可能发生。
     * cluster模式无法使用
     *
     * @param buckets 一个或多个 key-value
     */
    public <V> void mset(Map<String, V> buckets) {
        redissonClient.getBuckets().set(buckets);
    }

    /**
     * 同时设置一个或多个 key-value 对，当且仅当所有给定 key 都不存在。
     * <p>
     * 即使只有一个给定 key 已存在， MSETNX 也会拒绝执行所有给定 key 的设置操作。
     * <p>
     * MSETNX 是原子性的，因此它可以用作设置多个不同 key 表示不同字段(field)的唯一性逻辑对象(unique logic object)，所有字段要么全被设置，要么全不被设置。
     * cluster模式无法使用
     *
     * @param buckets 一个或多个 key-value
     * @return 当所有 key 都成功设置，返回 true 。如果所有给定 key 都设置失败(至少有一个 key 已经存在)，那么返回 false
     */
    public <V> boolean msetnx(Map<String, V> buckets) {
        return redissonClient.getBuckets().trySet(buckets);
    }

    /**
     * 将值 value 关联到 key ，并将 key 的生存时间设为 timeToLive (以 TimeUnit 为单位)。
     * <p>
     * 如果 key 已经存在， SETEX 命令将覆写旧值。
     *
     * @param key        键名
     * @param value      键值
     * @param timeToLive 过期时间
     * @param timeUnit   时间单位
     */
    public void setex(String key, String value, long timeToLive, TimeUnit timeUnit) {
        redissonClient.getBucket(key).set(value, timeToLive, timeUnit);
    }

    /**
     * 将字符串值 value 关联到 key 。
     * value 不能为 null，如果传 null 相当于执行 del 命令
     * <p>
     * 如果 key 已经持有其他值， SET 就覆写旧值，无视类型。
     * <p>
     * 对于某个原本带有生存时间（TTL）的键来说， 当 SET 命令成功在这个键上执行时， 这个键原有的 TTL 将被清除。
     *
     * @param key   键名
     * @param value 键值
     */
    public <V> void set(String key, V value) {
        redissonClient.getBucket(key).set(value);
    }

    /**
     * 将 key 的值设为 value ，当且仅当 key 不存在。
     * <p>
     * 若给定的 key 已经存在，则 SETNX 不做任何动作。
     * <p>
     * SETNX 是『SET if Not eXists』(如果不存在，则 SET)的简写。
     * <p>
     * 命令在指定的 key 不存在时，为 key 设置指定的值, expire 过期时间 timeUnit 过期时间单位
     *
     * @param key      键名
     * @param value    键值
     * @param expire   过期时间
     * @param timeUnit 时间单位
     * @return 是否成功
     */
    public <V> boolean setnx(String key, V value, long expire, TimeUnit timeUnit) {
        return redissonClient.getBucket(key).trySet(value, expire, timeUnit);
    }

    /**
     * 将 key 的值设为 value ，当且仅当 key 不存在。
     * <p>
     * 若给定的 key 已经存在，则 SETNX 不做任何动作。
     * <p>
     * SETNX 是『SET if Not eXists』(如果不存在，则 SET)的简写。
     *
     * @param key   键名
     * @param value 键值
     * @param <T>   键值类型
     * @return 是否成功
     */
    public <T> boolean setnx(String key, T value) {
        return redissonClient.getBucket(key).trySet(value);
    }

    /**
     * 返回 key 所储存的字符串值的长度。
     * <p>
     * 当 key 储存的不是字符串值时，返回一个错误。
     * 当 key 不存在时，返回 0
     *
     * @param key 键名
     * @return 字符串值的长度
     */
    public long strlen(String key) {
        RBucket<String> bucket = redissonClient.getBucket(key);
        return bucket.size();
    }

    //----- string

    //----- map
    private <V> RMap<String, V> getMap(String key) {
        return redissonClient.getMap(key);
    }

    /**
     * 删除哈希表 key 中的一个或多个指定域，不存在的域将被忽略。
     * <p>
     * 时间复杂度：O(N)， N 为要删除的域的数量。
     *
     * @param key    键名
     * @param fields 一个或多个域
     * @return 返回从map中删除的field数，不包括指定但不存在的field
     */
    public long hdel(String key, String... fields) {
        return getMap(key).fastRemove(fields);
    }

    /**
     * 查看哈希表 key 中，给定域 field 是否存在
     * <p>
     * 时间复杂度：O(1)
     *
     * @param key   键名
     * @param field 域
     * @return 如果哈希表含有给定域，返回 true 。如果哈希表不含有给定域，或 key 不存在，返回 false 。
     */
    public boolean hexists(String key, String field) {
        return getMap(key).containsKey(field);
    }

    /**
     * 返回哈希表 key 中给定域 field 的值。
     *
     * @param key   键名
     * @param field 域
     * @param <V>   值的类型
     * @return 给定域的值。当给定域不存在或是给定 key 不存在时，返回 null 。
     */
    public <V> V hget(String key, String field) {
        RMap<String, V> map = getMap(key);
        return map.get(field);
    }

    /**
     * 返回哈希表 key 中，所有的域和值。
     *
     * @param key 键值
     * @param <V> 值的类型
     * @return 返回域和值的map结构
     */
    public <V> Map<String, V> hgetAll(String key) {
        RMap<String, V> map = getMap(key);
        return map.readAllMap();
    }

    /**
     * 为哈希表 key 中的域 field 的值加上增量 increment 。
     * <p>
     * 增量也可以为负数，相当于对给定域进行减法操作。
     * <p>
     * 如果 key 不存在，一个新的哈希表被创建并执行 HINCRBY 命令。
     * <p>
     * 如果域 field 不存在，那么在执行命令前，域的值被初始化为 0 。
     * <p>
     * 对一个储存字符串值的域 field 执行 HINCRBY 命令将造成一个错误。
     * <p>
     * 时间复杂度：O(1)
     *
     * @param key    键名
     * @param field  域
     * @param number 增/减量
     * @param <V>    值的类型
     * @return 更新后的值
     */
    public <V> V incrOrDecr(String key, String field, Number number) {
        RMap<String, V> map = getMap(key);
        return map.addAndGet(field, number);
    }


    /**
     * 返回哈希表 key 中的所有域 field
     * O(N)， N 为哈希表的大小。
     *
     * @param key 键名
     * @return 返回哈希表 key 中的所有域 field
     */
    public <V> Set<String> hkeys(String key) {
        RMap<String, V> map = getMap(key);
        return map.readAllKeySet();
    }

    /**
     * 返回哈希表 key 中域的数量
     *
     * @param key 键名
     * @return 返回哈希表 key 中域的数量
     */
    public int hlen(String key) {
        return getMap(key).size();
    }

    /**
     * 返回哈希表 key 中，一个或多个给定域的值。
     * <p>
     * 如果给定的域不存在于哈希表，那么返回一个 null 值。
     *
     * @param key    键名
     * @param fields 域的set集合
     * @return 返回域-值的键值对
     */
    public <V> Map<String, V> hmget(String key, Set<String> fields) {
        RMap<String, V> map = getMap(key);
        return map.getAll(fields);
    }

    /**
     * 同时将多个 field-value (域-值)对设置到哈希表 key 中。
     * <p>
     * 此命令会覆盖哈希表中已存在的域。
     * <p>
     * 如果 key 不存在，一个空哈希表被创建并执行 HMSET 操作。
     *
     * @param key           键名
     * @param fieldValueMap 要存储在此映射中的键值对
     */
    public <V> void hmset(String key, Map<String, V> fieldValueMap) {
        getMap(key).putAll(fieldValueMap);
    }

    /**
     * 将哈希表 key 中的域 field 的值设为 value 。
     * <p>
     * 如果 key 不存在，一个新的哈希表被创建并进行 HSET 操作。
     * <p>
     * 如果域 field 已经存在于哈希表中，旧值将被覆盖。
     *
     * @param key   键名
     * @param field 域
     * @param value 值
     */
    public <V> boolean hset(String key, String field, V value) {
        RMap<String, V> map = getMap(key);
        return map.fastPut(field, value);
    }

    /**
     * 将哈希表 key 中的域 field 的值设置为 value ，当且仅当域 field 不存在。
     * <p>
     * 若域 field 已经存在，该操作无效。
     * <p>
     * 如果 key 不存在，一个新哈希表被创建并执行 HSETNX 命令。
     *
     * @param key   键名
     * @param field 域
     * @param value 值
     * @return 是否设置成功
     */
    public <V> boolean hsetnx(String key, String field, V value) {
        RMap<String, V> map = getMap(key);
        return map.fastPutIfAbsent(field, value);
    }

    /**
     * 获取hash中指定field对应value的字节长度
     *
     * @param key   键名
     * @param field 域
     * @return value的字节长度
     */
    public int hstrlen(String key, String field) {
        return getMap(key).valueSize(field);
    }

    /**
     * 返回哈希表 key 中所有域的值。
     *
     * @param key 键名
     * @return 值的集合
     */
    public <V> Collection<V> hvals(String key) {
        RMap<String, V> map = getMap(key);
        return map.readAllValues();
    }
    //----- map

    //----- list
    private <T> RList<T> getList(String key) {
        return redissonClient.getList(key);
    }

    /**
     * 检索并删除此队列的头，在必要时等待指定的等待时间，以使元素可用。
     *
     * @param key      键名
     * @param timeout  等待时间
     * @param timeUnit 时间单位
     * @param <E>      元素的类型
     * @return 队列的头元素
     * @throws InterruptedException 中断异常
     */
    public <E> E blpop(String key, long timeout, TimeUnit timeUnit) throws InterruptedException {
        RBlockingQueue<E> blockingQueue = redissonClient.getBlockingQueue(key);
        return blockingQueue.poll(timeout, timeUnit);
    }

    /**
     * 检索并删除此deque的最后一个元素，如果需要，则等待指定的等待时间直到元素可用。
     *
     * @param key      键名
     * @param timeout  等待时间
     * @param timeUnit 时间单位
     * @param <E>      元素的类型
     * @return 此deque的尾部，如果指定的等待时间过了，则返回null
     * @throws InterruptedException 中断异常
     */
    public <E> E brpop(String key, long timeout, TimeUnit timeUnit) throws InterruptedException {
        RBlockingDeque<E> blockingDeque = redissonClient.getBlockingDeque(key);
        return blockingDeque.pollLast(timeout, timeUnit);
    }

    /**
     * 返回列表 key 中，下标为 index 的元素。
     * <p>
     * 下标(index)参数 start 和 stop 都以 0 为底，也就是说，以 0 表示列表的第一个元素，以 1 表示列表的第二个元素，以此类推。
     * <p>
     * 你也可以使用负数下标，以 -1 表示列表的最后一个元素， -2 表示列表的倒数第二个元素，以此类推。
     * <p>
     * 如果 key 不是列表类型，返回一个错误。
     * <p>
     * 时间复杂度：
     * O(N)， N 为到达下标 index 过程中经过的元素数量。
     * 因此，对列表的头元素和尾元素执行 LINDEX 命令，复杂度为O(1)。
     *
     * @param key   键名
     * @param index 下标
     * @return 列表中下标为 index 的元素。如果 index 参数的值不在列表的区间范围内(out of range)，返回 null 。
     */
    public <E> E lindex(String key, int index) {
        RList<E> list = getList(key);
        return list.get(index);
    }

    /**
     * 将值 value 插入到列表 key 当中的目标值之前或之后
     * 返回经过插入操作后的list长度，或者当目标值找不到的时候返回 -1
     *
     * @param key           键名
     * @param targetElement 目标元素
     * @param element       要添加的元素
     * @param direction     DIRECTION.AFTER 或者 DIRECTION.BEFORE
     * @return 存在多个目标值或者目标值不存在返回-1，插入成功返回list元素个数
     */
    public <E> int linsert(String key, E targetElement, DIRECTION direction, E element) {
        RList<E> list = getList(key);
        int result = 0;
        if (direction.equals(DIRECTION.AFTER)) {
            result = list.addAfter(targetElement, element);
        }
        if (direction.equals(DIRECTION.BEFORE)) {
            result = list.addBefore(targetElement, element);
        }
        return result;
    }

    /**
     * 返回列表 key 的长度。
     * <p>
     * 如果 key 不存在，则 key 被解释为一个空列表，返回 0 .
     * <p>
     * 如果 key 不是列表类型，返回一个错误。
     *
     * @param key 键名
     * @return 返回列表 key 的长度
     */
    public int llen(String key) {
        return getList(key).size();
    }

    /**
     * 移除并返回列表 key 的头元素。
     * 时间复杂度：O(1)
     *
     * @param key 键名
     */
    public <E> E lpop(String key) {
        return (E) redissonClient.getBlockingDeque(key).pollFirst();
    }

    /**
     * 将一个或多个值 value 插入到列表 key 的表头
     * <p>
     * 如果有多个 value 值，那么各个 value 值按从左到右的顺序依次插入到表头： 比如说，对空列表 mylist 执行命令 LPUSH mylist a b c ，列表的值将是 c b a ，这等同于原子性地执行 LPUSH mylist a 、 LPUSH mylist b 和 LPUSH mylist c 三个命令。
     * <p>
     * 如果 key 不存在，一个空列表会被创建并执行 LPUSH 操作。
     * <p>
     * 当 key 存在但不是列表类型时，返回一个错误。
     *
     * @param key      键名
     * @param elements 要插入的元素
     * @param <E>      元素的类型
     * @return 是否插入成功
     */
    public <E> boolean lpush(String key, E... elements) {
        RList<E> list = getList(key);
        return list.addAll(0, Arrays.asList(elements));
    }

    /**
     * 返回从fromIndex到toIndex的值。索引是从零开始的。-1表示最后一个元素，-2表示倒数第二个元素，以此类推
     *
     * @param key       键名
     * @param fromIndex 起始索引
     * @param toIndex   结束索引
     * @param <E>       元素的类型
     * @return 返回检索到的结果list
     */
    public <E> List<E> lrange(String key, int fromIndex, int toIndex) {
        RList<E> list = getList(key);
        return list.range(fromIndex, toIndex);
    }

    /**
     * 从存于 key 的列表里移除前 count 次出现的值为 value 的元素。 这个 count 参数通过下面几种方式影响这个操作：
     * <p>
     * count > 0: 从头往尾移除值为 value 的元素。
     * count < 0: 从尾往头移除值为 value 的元素。
     * count = 0: 移除所有值为 value 的元素。
     * 比如， LREM list -2 “hello” 会从存于 list 的列表里移除最后两个出现的 “hello”。
     * <p>
     * 需要注意的是，如果list里没有存在key就会被当作空list处理，所以当 key 不存在的时候，这个命令会返回 false。如果不是list类型会返回错误
     *
     * @param key     键名
     * @param count   次数
     * @param element 要删除的元素
     */
    public <E> boolean lrem(String key, E element, int count) {
        RList<E> list = getList(key);
        return list.remove(element, count);
    }

    /**
     * 设置 index 位置的list元素的值为 value
     * 当index超出范围时会返回一个error。
     * 注意执行命令的key必须是已经存在的list结构
     *
     * @param key     键名
     * @param index   对象的索引
     * @param element 要设置的元素
     */
    public <E> void lset(String key, int index, E element) {
        getList(key).fastSet(index, element);
    }

    /**
     * 对一个列表进行修剪(trim)，保留区间 [fromIndex, toIndex] 内的元素
     * 修剪(trim)一个已存在的 list，这样 list 就会只包含指定范围的指定元素。start 和 stop 都是由0开始计数的， 这里的 0 是列表里的第一个元素（表头），1 是第二个元素，以此类推。
     * <p>
     * 例如： LTRIM foobar 0 2 将会对存储在 foobar 的列表进行修剪，只保留列表里的前3个元素。
     * <p>
     * start 和 end 也可以用负数来表示与表尾的偏移量，比如 -1 表示列表里的最后一个元素， -2 表示倒数第二个，等等。
     * <p>
     * 超过范围的下标并不会产生错误：如果 start 超过列表尾部，或者 start > end，结果会是列表变成空表（即该 key 会被移除）。 如果 end 超过列表尾部，Redis 会将其当作列表的最后一个元素。
     * <p>
     * LTRIM 的一个常见用法是和 LPUSH / RPUSH 一起使用。 例如：
     * <p>
     * LPUSH mylist someelement
     * LTRIM mylist 0 99
     * 这一对命令会将一个新的元素 push 进列表里，并保证该列表不会增长到超过100个元素。这个是很有用的，比如当用 Redis 来存储日志。 需要特别注意的是，当用这种方式来使用 LTRIM 的时候，操作的复杂度是 O(1) ， 因为平均情况下，每次只有一个元素会被移除。
     * <p>
     * 修剪列表并只保留从mindex(包含)到toIndex(包含)的指定范围内的元素。
     *
     * @param key       键名
     * @param fromIndex 起始索引
     * @param toIndex   结束索引
     */
    public void ltrim(String key, int fromIndex, int toIndex) {
        getList(key).trim(fromIndex, toIndex);
    }

    /**
     * 移除列表 key 的尾元素
     *
     * @param key 键名
     */
    public <E> E rpop(String key) {
        return (E) redissonClient.getBlockingDeque(key).pollLast();
    }

    /**
     * 将一个值 value 插入到列表 key 的表尾(最右边)
     *
     * @param key list key
     */
    public <E> void rpush(String key, E... element) {
        getList(key).addAll(getList(key).size(), Arrays.asList(element));
    }
    //----- list

    //----- set
    private <V> RSet<V> getSet(String key) {
        return redissonClient.getSet(key);
    }

    /**
     * 将一个或多个 member 元素加入到集合 key 当中，已经存在于集合的 member 元素将被忽略。
     * <p>
     * 假如 key 不存在，则创建一个只包含 member 元素作成员的集合。
     * <p>
     * 当 key 不是集合类型时，返回一个错误。
     *
     * @param key    键名
     * @param values 要添加的值
     */
    public <V> boolean sadd(String key, V... values) {
        RSet<V> set = getSet(key);
        return set.addAll(Arrays.asList(values));
    }

    /**
     * 返回集合 key 的基数(集合中元素的数量)。
     *
     * @param key 键名
     * @return 集合中元素的数量
     */
    public int scard(String key) {
        return getSet(key).size();
    }

    /**
     * 返回一个集合的全部成员，该集合是所有给定集合之间的差集。
     * cluster模式无法使用
     *
     * @param key  键名
     * @param keys 其他集合的键名
     * @return 返回该集合的全部成员
     */
    public <V> Set<V> sdiff(String key, String... keys) {
        RSet<V> set = getSet(key);
        return set.readDiff(keys);
    }

    /**
     * 这个命令与SINTER命令类似, 但是它并不是直接返回结果集,而是将结果保存在 destination集合中.
     * 如果destination 集合存在, 则会被重写.
     * cluster模式无法使用
     *
     * @param destinationKey 目标键名
     * @param keys           其他键名
     * @return 返回目标键集合的全部成员
     */
    public int sdiffStore(String destinationKey, String... keys) {
        return getSet(destinationKey).diff(keys);
    }

    /**
     * 返回一个集合的全部成员，该集合是所有给定集合的交集。
     * <p>
     * 不存在的 key 被视为空集。
     * <p>
     * 当给定集合当中有一个空集时，结果也为空集(根据集合运算定律)。.
     * cluster模式无法使用
     *
     * @param key  键名
     * @param keys 其他键
     * @return 交集后所有元素
     */
    public <V> Set<V> sinter(String key, String... keys) {
        RSet<V> set = getSet(key);
        return set.readIntersection(keys);
    }

    /**
     * 返回指定所有的集合的成员的交集.
     * cluster模式无法使用
     *
     * @param destinationKey 目标键
     * @param keys           其他键
     * @return 交集的大小
     */
    public <V> int sinterStore(String destinationKey, String... keys) {
        RSet<V> set = getSet(destinationKey);
        return set.intersection(keys);
    }

    /**
     * 判断集合 key 是否包含 member 元素
     *
     * @param key   键名
     * @param value 元素
     * @return 是否包含
     */
    public <V> boolean sisMember(String key, V value) {
        return getSet(key).contains(value);
    }

    /**
     * 返回集合 key 中的所有成员。
     * <p>
     * 不存在的 key 被视为空集合。
     * cluster模式无法使用
     *
     * @param key 键名
     * @return 集合 key 中的所有成员
     */
    public <V> Set<V> smembers(String key) {
        RSet<V> set = getSet(key);
        return set.readAll();
    }

    /**
     * 将该集合中的成员移动到给定的目标集合中
     * cluster 模式无法使用
     *
     * @param key         键名
     * @param destination 目标键
     * @param value       要移动的元素
     * @param <V>         元素的类型
     * @return 是否移动成功
     */
    public <V> boolean smove(String key, String destination, V value) {
        RSet<V> set = getSet(key);
        return set.move(destination, value);
    }

    /**
     * 移除并返回集合中的一个随机元素
     *
     * @param key   键名
     * @param count 数量
     * @return set
     */
    public <V> Set<V> spop(String key, int count) {
        RSet<V> set = getSet(key);
        return set.removeRandom(count);
    }

    /**
     * 移除集合 key 中的一个或多个 member 元素，不存在的 member 元素会被忽略
     * 注意删除元素的个数
     *
     * @param key      键名
     * @param elements 要删除的元素
     * @return 是否操作成功
     */
    public <V> boolean srem(String key, V... elements) {
        RSet<V> set = getSet(key);
        return set.removeAll(Arrays.asList(elements));
    }

    /**
     * 返回一个集合的全部成员，该集合是所有给定集合的并集
     * cluster模式无法使用
     *
     * @param key  键名
     * @param keys 其他键
     * @param <V>  元素类型
     * @return 并集
     */
    public <V> Set<V> sunion(String key, String... keys) {
        RSet<V> set = getSet(key);
        return set.readUnion(keys);
    }

    /**
     * 返回一个集合的全部成员，该集合是所有给定集合的并集，指定的key是参数destination
     * cluster模式无法使用
     *
     * @param destination 目标键
     * @param keys        其他键
     * @return 并集的大小
     */
    public int sunionStore(String destination, String... keys) {
        return getSet(destination).union(keys);
    }

    //----- set

    //----- zset
    private <V> RScoredSortedSet<V> getScoreSortedSet(String key) {
        return redissonClient.getScoredSortedSet(key);
    }

    /**
     * 将一个或多个 member 元素及其 score 值加入到有序集 key 当中。
     * <p>
     * 如果某个 member 已经是有序集的成员，那么更新这个 member 的 score 值，并通过重新插入这个 member 元素，来保证该 member 在正确的位置上。
     * <p>
     * score 值可以是整数值或双精度浮点数。
     * <p>
     * 如果 key 不存在，则创建一个空的有序集并执行 ZADD 操作。
     * <p>
     * 当 key 存在但不是有序集类型时，返回一个错误。
     * 返回插入成功的元素个数，如果已存在元素就忽略
     *
     * @param key    键名
     * @param values value-score
     * @param <V>    值的类型
     */
    public <V> int zadd(String key, Map<V, Double> values) {
        RScoredSortedSet<V> scoreSortedSet = getScoreSortedSet(key);
        return scoreSortedSet.addAll(values);
    }

    /**
     * 返回有序集合 key 中元素的数量
     *
     * @param key 键名
     * @return 元素的数量
     */
    public int zcard(String key) {
        return getScoreSortedSet(key).size();
    }

    /**
     * 返回有序集 key 中， score 值在 min 和 max 之间(默认包括 score 值等于 min 或 max )的成员的数量
     *
     * @param key                 键名
     * @param startScore          min
     * @param startScoreInclusive 是否包含min
     * @param endScore            max
     * @param endScoreInclusive   是否包含max
     * @param <V>                 值的类型
     * @return 符合条件的元素的数量
     */
    public <V> int zcount(String key, double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive) {
        RScoredSortedSet<V> scoreSortedSet = getScoreSortedSet(key);
        return scoreSortedSet.count(startScore, startScoreInclusive, endScore, endScoreInclusive);
    }

    /**
     * 为有序集 key 的成员 member 的 score 值加上增量 increment 。
     * <p>
     * 可以通过传递一个负数值 increment ，让 score 减去相应的值，比如 ZINCRBY key -5 member ，就是让 member 的 score 值减去 5 。
     * <p>
     * 当 key 不存在，或 member 不是 key 的成员时， ZINCRBY key increment member 等同于 ZADD key increment member 。
     * <p>
     * 当 key 不是有序集类型时，返回一个错误。
     * <p>
     * score 值可以是整数值或双精度浮点数。
     *
     * @param key       键名
     * @param member    成员
     * @param increment 增加/减少
     * @param <V>       成员类型
     * @return member 成员的新 score 值
     */
    public <V> Double zincrby(String key, V member, Number increment) {
        RScoredSortedSet<V> sortedSet = redissonClient.getScoredSortedSet(key);
        return sortedSet.addScore(member, increment);
    }

    /**
     * 按照分数从小到大排序
     * 按等级范围返回值。索引是从零开始的。-1表示最高分，-2表示次高分。
     *
     * @param key        键名
     * @param startIndex 起始索引
     * @param endIndex   结束索引
     * @param <V>        成员类型
     * @return 成员列表
     */
    public <V> Collection<V> zrange(String key, int startIndex, int endIndex) {
        RScoredSortedSet<V> sortedSet = redissonClient.getScoredSortedSet(key);
        return sortedSet.valueRange(startIndex, endIndex);
    }

    /**
     * 返回startScore和endScore之间的所有值
     *
     * @param key                 键名
     * @param startScore          开始分
     * @param startScoreInclusive 是否包含开始分
     * @param endScore            结束分
     * @param endScoreInclusive   是否包含结束分
     * @param <V>                 成员类型
     * @return 结果集合
     */
    public <V> Collection<V> zrangeByScore(String key, double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive) {
        RScoredSortedSet<V> sortedSet = redissonClient.getScoredSortedSet(key);
        return sortedSet.valueRange(startScore, startScoreInclusive, endScore, endScoreInclusive);
    }


    /**
     * 返回值的排序，分数从低到高。
     * 排序从0开始，分数最低的排序是0
     *
     * @param key    键名
     * @param member 成员
     * @param <V>    成员类型
     * @return 成员的排名
     */
    public <V> Integer zrank(String key, V member) {
        RScoredSortedSet<V> sortedSet = redissonClient.getScoredSortedSet(key);
        return sortedSet.rank(member);
    }


    /**
     * 移除有序集 key 中的一个或多个成员，不存在的成员将被忽略。
     * <p>
     * 当 key 存在但不是有序集类型时，返回一个错误。
     *
     * @param key     键名
     * @param members 成员
     * @param <V>     成员类型
     * @return 是否删除成功
     */
    public <V> boolean zrem(String key, V... members) {
        RScoredSortedSet<V> sortedSet = redissonClient.getScoredSortedSet(key);
        return sortedSet.removeAll(Arrays.asList(members));
    }

    /**
     * 按指定排名范围移除值。索引是从零开始的。-1表示最高分，-2表示次高分。
     *
     * @param key        键名
     * @param startIndex 起始索引
     * @param endIndex   结束索引
     * @return 删除的元素数量
     */
    public int zremRangeByRank(String key, int startIndex, int endIndex) {
        return redissonClient.getScoredSortedSet(key).removeRangeByRank(startIndex, endIndex);
    }

    /**
     * 删除startScore和endScore之间的所有值
     *
     * @param key                 键名
     * @param startScore          开始分
     * @param startScoreInclusive 是否包含开始分
     * @param endScore            结束分
     * @param endScoreInclusive   是否包含结束分
     * @return 删除的元素数量
     */
    public int zremRangeByScore(String key, double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive) {
        return redissonClient.getScoredSortedSet(key).removeRangeByScore(startScore, startScoreInclusive, endScore, endScoreInclusive);
    }

    /**
     * 按反向顺序按等级范围返回值。索引是从零开始的。-1表示最高分，-2表示次高分。
     *
     * @param key        键名
     * @param startIndex 起始索引
     * @param endIndex   结束索引
     * @param <V>        元素类型
     * @return 元素集合
     */
    public <V> Collection<V> zrevRange(String key, int startIndex, int endIndex) {
        RScoredSortedSet<V> sortedSet = redissonClient.getScoredSortedSet(key);
        return sortedSet.valueRangeReversed(startIndex, endIndex);
    }

    /**
     * 以倒序返回startScore和endScore之间的所有值。
     *
     * @param key                 键名
     * @param startScore          开始分
     * @param startScoreInclusive 是否包含开始分
     * @param endScore            结束分
     * @param endScoreInclusive   是否包含结束分
     * @param <V>                 元素类型
     * @return 元素集合
     */
    public <V> Collection<V> zrevRangeByScore(String key, double startScore, boolean startScoreInclusive, double endScore, boolean endScoreInclusive) {
        RScoredSortedSet<V> sortedSet = redissonClient.getScoredSortedSet(key);
        return sortedSet.valueRangeReversed(startScore, startScoreInclusive, endScore, endScoreInclusive);
    }

    /**
     * 返回值的排序，分数从高到低。 score 值最大的成员排名为 0
     *
     * @param key    键名
     * @param member 成员
     * @param <V>    成员类型
     * @return 成员的排名
     */
    public <V> Integer zrevRank(String key, V member) {
        RScoredSortedSet<V> sortedSet = redissonClient.getScoredSortedSet(key);
        return sortedSet.revRank(member);
    }

    /**
     * 返回元素的分值，如果元素不存在则返回null。
     *
     * @param key    键名
     * @param member 成员
     * @param <V>    成员类型
     * @return 分值
     */
    public <V> Double zscore(String key, V member) {
        RScoredSortedSet<V> sortedSet = redissonClient.getScoredSortedSet(key);
        return sortedSet.getScore(member);
    }


    /**
     * 计算给定的一个或多个有序集的并集，将该并集(结果集)储存到 destination 。
     * <p>
     * 默认情况下，结果集中某个成员的 score 值是所有给定集下该成员 score 值之 和 。
     * cluster模式不支持
     *
     * @param destinationKey 目标有序集的key
     * @param keys           一个或多个有序集的key
     * @return 返回目标集的长度
     */
    public int zunionStore(String destinationKey, String... keys) {
        return redissonClient.getScoredSortedSet(destinationKey).union(keys);
    }


    /**
     * 计算给定的一个或多个有序集的交集，将该交集(结果集)储存到 destination 。
     * <p>
     * 默认情况下，结果集中某个成员的 score 值是所有给定集下该成员 score 值之和.
     * cluster模式不支持
     *
     * @param destinationKey 目标有序集的key
     * @param keys           一个或多个有序集的key
     * @return 返回目标集的长度
     */
    public int zinterStore(String destinationKey, String... keys) {
        return redissonClient.getScoredSortedSet(destinationKey).intersection(keys);
    }
    //----- zset

    //----- lock

    /**
     * 分布式锁实现 无返回值
     * 获取不到锁会阻塞等待获取到锁
     *
     * @param lockName 锁名称
     * @param handle   业务处理
     */
    @Transactional(rollbackFor = Exception.class)
    public void lock(String lockName, VoidHandle handle) {
        RLock rLock = getLock(lockName);
        try {
            rLock.lock();
            log.info("锁名称 {}，获取锁成功", lockName);
            handle.execute();
        } finally {
            rLock.unlock();
        }
    }

    /**
     * 分布式锁实现,带有锁释放时间，无返回值
     * 获取不到锁会阻塞等待获取到锁
     *
     * @param lockName  锁名称
     * @param leaseTime 锁释放时间
     * @param unit      时间单位
     * @param handle    业务处理
     */
    @Transactional(rollbackFor = Exception.class)
    public void lock(String lockName, long leaseTime, TimeUnit unit, VoidHandle handle) {
        RLock rLock = getLock(lockName);
        try {
            rLock.lock(leaseTime, unit);
            log.info("锁名称{}，获取锁成功", lockName);
            handle.execute();
        } finally {
            rLock.unlock();
        }
    }

    /**
     * 带返回值分布式锁实现
     * 获取不到锁会阻塞等待获取到锁
     *
     * @param lockName 锁名称
     * @param handle   业务处理
     * @param <T>      返回值
     * @return T
     */
    @Transactional(rollbackFor = Exception.class)
    public <T> T lock(String lockName, ReturnHandle<T> handle) {
        RLock rLock = getLock(lockName);
        try {
            rLock.lock();
            log.info("锁名称{}，获取锁成功", lockName);
            return handle.execute();
        } finally {
            rLock.unlock();
        }
    }

    /**
     * 带返回值分布式锁实现,带有锁释放时间参数
     * 获取不到锁会阻塞等待获取到锁
     *
     * @param lockName  锁名称
     * @param leaseTime 锁释放时间
     * @param unit      时间单位
     * @param handle    业务处理
     * @param <T>       返回值类型
     * @return 返回值
     */
    @Transactional(rollbackFor = Exception.class)
    public <T> T lock(String lockName, long leaseTime, TimeUnit unit, ReturnHandle<T> handle) {
        RLock rLock = getLock(lockName);
        try {
            rLock.lock(leaseTime, unit);
            log.info("锁名称 {}，获取锁成功", lockName);
            return handle.execute();
        } finally {
            rLock.unlock();
        }
    }

    /**
     * 分布式锁实现
     * 获取不到锁会直接返回
     *
     * @param lockName 锁名称
     * @param handle   业务处理
     */
    @Transactional(rollbackFor = Exception.class)
    public void tryLock(String lockName, VoidHandle handle) {
        RLock rLock = getLock(lockName);
        if (!rLock.tryLock()) {
            log.error("锁名称 {}，获取锁失败，返回", lockName);
            return;
        }

        try {
            log.info("锁名称 {}，获取锁成功", lockName);
            handle.execute();
        } finally {
            rLock.unlock();
        }
    }

    /**
     * 分布式锁实现，带锁等待时间，锁释放时间参数
     * 获取不到锁会直接返回
     *
     * @param lockName  锁名称
     * @param waitTime  锁等待时间
     * @param leaseTime 锁释放时间
     * @param unit      时间单位
     * @param handle    业务逻辑
     * @throws InterruptedException 中断异常
     */
    @Transactional(rollbackFor = Exception.class)
    public void tryLock(String lockName, long waitTime, long leaseTime, TimeUnit unit, VoidHandle handle) throws InterruptedException {
        RLock rLock = getLock(lockName);
        if (!rLock.tryLock(waitTime, leaseTime, unit)) {
            log.error("锁名称 {}，获取锁失败，返回", lockName);
            return;
        }

        try {
            log.info("锁名称 {}，获取锁成功", lockName);
            handle.execute();
        } finally {
            rLock.unlock();
        }
    }

    /**
     * 带返回值分布式锁实现
     * 获取不到锁返回null
     *
     * @param lockName 锁名称
     * @param handle   业务处理
     * @param <T>      返回值
     * @return T
     */
    @Transactional(rollbackFor = Exception.class)
    public <T> T tryLock(String lockName, ReturnHandle<T> handle) {
        RLock rLock = getLock(lockName);
        if (!rLock.tryLock()) {
            log.error("锁名称 {}，获取锁失败，返回null", lockName);
            return null;
        }

        try {
            log.info("锁名称 {}，获取锁成功", lockName);
            return handle.execute();
        } finally {
            rLock.unlock();
        }
    }

    /**
     * 带返回值分布式锁实现，带锁等待时间，锁释放时间参数
     * 获取不到锁返回null
     *
     * @param lockName  锁名称
     * @param waitTime  锁等待时间
     * @param leaseTime 锁释放时间
     * @param unit      时间单位
     * @param handle    业务逻辑
     * @param <T>       返回值类型
     * @return 返回值
     * @throws InterruptedException 中断异常
     */
    @Transactional(rollbackFor = Exception.class)
    public <T> T tryLock(String lockName, long waitTime, long leaseTime, TimeUnit unit, ReturnHandle<T> handle) throws InterruptedException {
        RLock rLock = getLock(lockName);
        if (!rLock.tryLock(waitTime, leaseTime, unit)) {
            log.error("锁名称 {}，获取锁失败，返回null", lockName);
            return null;
        }

        try {
            log.info("锁名称 {}，获取锁成功", lockName);
            return handle.execute();
        } finally {
            rLock.unlock();
        }
    }


    /**
     * 分布式锁实现
     * 获取不到锁，抛异常处理
     *
     * @param lockName 锁名称
     * @param handle   业务处理
     */
    @Transactional(rollbackFor = Exception.class)
    public void tryLockException(String lockName, VoidHandle handle) {
        RLock rLock = getLock(lockName);
        if (!rLock.tryLock()) {
            log.error("锁名称 {}，获取锁失败，抛异常处理", lockName);
            throw new RuntimeException("处理中");
        }

        try {
            log.info("锁名称 {}，获取锁成功", lockName);
            handle.execute();
        } finally {
            rLock.unlock();
        }
    }

    /**
     * 分布式锁实现，带锁等待时间，锁释放时间参数
     * 获取不到锁，抛异常处理
     *
     * @param lockName  锁名称
     * @param waitTime  锁等待时间
     * @param leaseTime 锁释放时间
     * @param unit      时间单位
     * @param handle    业务逻辑
     * @throws InterruptedException 中断异常
     */
    @Transactional(rollbackFor = Exception.class)
    public void tryLockException(String lockName, long waitTime, long leaseTime, TimeUnit unit, VoidHandle handle) throws InterruptedException {
        RLock rLock = getLock(lockName);
        if (!rLock.tryLock(waitTime, leaseTime, unit)) {
            log.error("锁名称 {}，获取锁失败，抛异常处理", lockName);
            throw new RuntimeException("处理中");
        }

        try {
            log.info("锁名称 {}，获取锁成功", lockName);
            handle.execute();
        } finally {
            rLock.unlock();
        }
    }

    /**
     * 带返回值分布式锁实现
     * 获取不到锁，抛异常处理
     *
     * @param lockName 锁名称
     * @param handle   业务处理
     * @param <T>      返回值
     * @return T
     */
    @Transactional(rollbackFor = Exception.class)
    public <T> T tryLockException(String lockName, ReturnHandle<T> handle) {
        RLock rLock = getLock(lockName);
        if (!rLock.tryLock()) {
            log.error("锁名称 {}，获取锁失败，抛异常处理", lockName);
            throw new RuntimeException("处理中");
        }

        try {
            log.info("锁名称 {}，获取锁成功", lockName);
            return handle.execute();
        } finally {
            rLock.unlock();
        }
    }

    /**
     * 带返回值分布式锁实现，带锁等待时间，锁释放时间参数
     * 获取不到锁，抛异常处理
     *
     * @param lockName  锁名称
     * @param waitTime  获取不到锁的等待时间
     * @param leaseTime 获取到锁后释放时间
     * @param unit      等待时间和释放时间的单位
     * @param handle    业务处理
     * @param <T>       返回值
     * @return 返回值
     * @throws InterruptedException 中断异常
     */
    @Transactional(rollbackFor = Exception.class)
    public <T> T tryLockException(String lockName, long waitTime, long leaseTime, TimeUnit unit, ReturnHandle<T> handle) throws InterruptedException {
        RLock rLock = getLock(lockName);
        if (!rLock.tryLock(waitTime, leaseTime, unit)) {
            log.error("锁名称 {}，获取锁失败，抛异常处理", lockName);
            throw new RuntimeException("处理中");
        }

        try {
            log.info("锁名称 {}，获取锁成功", lockName);
            return handle.execute();
        } finally {
            rLock.unlock();
        }
    }

    /**
     * 获取锁
     *
     * @param lockName 锁名称
     * @return 锁对象
     */
    private RLock getLock(String lockName) {
        log.info("获取分布式锁lockName:{}", lockName);
        if (StringUtils.isEmpty(lockName)) {
            throw new RuntimeException("分布式锁KEY为空");
        }
        return redissonClient.getLock(lockName);
    }
    //----- lock

    //----- hyperLogLog

    /**
     * 将一个或多个元素添加到此结构中
     *
     * @param key      键名
     * @param elements 要添加的元素
     * @param <E>      元素类型
     * @return 如果至少添加了一个对象，则为True;如果已经添加了所有对象，则为false
     */
    public <E> boolean pfadd(String key, E... elements) {
        RHyperLogLog<E> hyperLogLog = redissonClient.getHyperLogLog(key);
        return hyperLogLog.addAll(Arrays.asList(elements));
    }

    /**
     * 返回添加到该结构中的唯一元素的近似数量
     *
     * @param key 键名
     * @return 添加到该结构中的唯一元素的近似数量
     */
    public long pfcount(String key) {
        return redissonClient.getHyperLogLog(key).count();
    }

    /**
     * 命令将多个 HyperLogLog 合并为一个 HyperLogLog ，合并后的 HyperLogLog 的基数估算值是通过对所有 给定 HyperLogLog 进行并集计算得出
     * cluster模式无法使用
     *
     * @param destLogKey   目标log key
     * @param otherLogKeys 需要合并的log keys
     */
    public void pfmerge(String destLogKey, String... otherLogKeys) {
        redissonClient.getHyperLogLog(destLogKey).mergeWith(otherLogKeys);
    }
    //----- hyperLogLog

    //----- geo

    /**
     * 添加成员坐标数据，一个或多个
     * <p>
     * double longitude 经度, double latitude 维度, Object member 成员
     *
     * @param key        键名
     * @param geoEntries geo 元素对象，包含经度，维度，成员
     * @return 添加到排序集的元素数，不包括已存在的更新分数的元素
     */
    public long geoAdd(String key, GeoEntry... geoEntries) {
        RGeo<GeoEntry> geo = redissonClient.getGeo(key);
        return geo.add(geoEntries);
    }

    /**
     * 获取指定成员的坐标
     *
     * @param key     键名
     * @param members 成员，一个或多个
     * @return 成员坐标数据  GeoPosition 包含坐标数据的对象
     */
    public <V> Map<V, GeoPosition> geoPos(String key, V... members) {
        RGeo<V> geo = redissonClient.getGeo(key);
        return geo.pos(members);
    }

    /**
     * 返回两个成员之间的距离，距离单位由GeoUnit指定。单位包含 km、m等
     *
     * @param key          键名
     * @param firstMember  第一个对象
     * @param secondMember 第二个对象
     * @param geoUnit      地质单位,距离单位
     * @return 返回距离
     */
    public <V> Double geoDist(String key, V firstMember, V secondMember, GeoUnit geoUnit) {
        RGeo<V> geo = redissonClient.getGeo(key);
        return geo.dist(firstMember, secondMember, geoUnit);
    }

    /**
     * 根据用户给定的经纬度坐标来获取指定范围内的地理位置集合
     * 经度——对象的经度 纬度——对象的纬度 半径——以地质单位表示的半径 geoUnit—地质单位 geoOrder——结果的顺序 计数——结果限制 返回值: 的对象列表
     *
     * @param key       键名
     * @param longitude 经度
     * @param latitude  维度
     * @param radius    半径
     * @param geoUnit   地质单位
     * @param geoOrder  结果的顺序
     * @param count     计数——结果限制
     * @param <V>       成员类型
     * @return 符合条件的结果
     */
    public <V> List<V> geoRadius(String key, double longitude, double latitude, double radius, GeoUnit geoUnit, GeoOrder geoOrder, int count) {
        RGeo<V> geo = redissonClient.getGeo(key);
        return geo.radius(longitude, latitude, radius, geoUnit, geoOrder, count);
    }

    public <V> List<V> geoRadius(String key, V member, double radius, GeoUnit geoUnit, GeoOrder geoOrder, int count) {
        RGeo<V> geo = redissonClient.getGeo(key);
        return geo.radius(member, radius, geoUnit, geoOrder, count);
    }

    public <V> Map<V, Double> geoRadiusWithDistance(String key, double longitude, double latitude, double radius, GeoUnit geoUnit, GeoOrder geoOrder, int count) {
        RGeo<V> geo = redissonClient.getGeo(key);
        return geo.radiusWithDistance(longitude, latitude, radius, geoUnit, geoOrder, count);
    }

    public <V> Map<V, Double> geoRadiusWithDistance(String key, V member, double radius, GeoUnit geoUnit, GeoOrder geoOrder, int count) {
        RGeo<V> geo = redissonClient.getGeo(key);
        return geo.radiusWithDistance(member, radius, geoUnit, geoOrder, count);
    }

    public long geoRadiusStoreSortedTo(String key, String destName, double longitude, double latitude, double radius, GeoUnit geoUnit, GeoOrder geoOrder, int count) {
        return redissonClient.getGeo(key).radiusStoreSortedTo(destName, longitude, latitude, radius, geoUnit, geoOrder, count);
    }

    public <V> long geoRadiusStoreSortedTo(String key, String destName, V member, double radius, GeoUnit geoUnit, GeoOrder geoOrder, int count) {
        return redissonClient.getGeo(key).radiusStoreSortedTo(destName, member, radius, geoUnit, geoOrder, count);
    }

    public <V> Map<V, GeoPosition> geoRadiusWithPosition(String key, double longitude, double latitude, double radius, GeoUnit geoUnit, GeoOrder geoOrder, int count) {
        RGeo<V> geo = redissonClient.getGeo(key);
        return geo.radiusWithPosition(longitude, latitude, radius, geoUnit, geoOrder, count);
    }

    public <V> Map<V, GeoPosition> geoRadiusWithPosition(String key, V member, double radius, GeoUnit geoUnit, GeoOrder geoOrder, int count) {
        RGeo<V> geo = redissonClient.getGeo(key);
        return geo.radiusWithPosition(member, radius, geoUnit, geoOrder, count);
    }

    public long geoRadiusStoreTo(String key, String destName, double longitude, double latitude, double radius, GeoUnit geoUnit, GeoOrder geoOrder, int count) {
        return redissonClient.getGeo(key).radiusStoreTo(destName, longitude, latitude, radius, geoUnit, geoOrder, count);
    }

    public <V> long geoRadiusStoreTo(String key, String destName, V member, double radius, GeoUnit geoUnit, GeoOrder geoOrder, int count) {
        return redissonClient.getGeo(key).radiusStoreTo(destName, member, radius, geoUnit, geoOrder, count);
    }
    //----- geo

    //----- bitmap

    /**
     * 返回设置为1的数量
     *
     * @param key 键值
     * @return 设置为1的数量
     */
    public long bitcount(String key) {
        return redissonClient.getBitSet(key).cardinality();
    }

    /**
     * SYMBOL.OR: 对该对象和指定的bitset执行OR操作。将结果存储到此对象中。
     * SYMBOL.AND: 对该对象和指定的bitset执行AND操作。将结果存储到此对象中。
     * SYMBOL.XOR: 对该对象和指定的位集执行异或操作。将结果存储到此对象中。
     * OR：或运算，只要运算中一方为1，则结果为1
     * XOR：异或运算，只要运算中一方为1，一方为0，则结果为1，否则为0
     * AND：与运算，运算中，必须双方都为1，其结果才为1，否则为0
     *
     * @param key         键名
     * @param symbol      符号
     * @param bitSetNames 存储bitset的名称
     */
    public void bitop(String key, SYMBOL symbol, String... bitSetNames) {
        RBitSet bitSet = redissonClient.getBitSet(key);
        if (SYMBOL.OR.equals(symbol)) {
            bitSet.or(bitSetNames);
        }
        if (SYMBOL.AND.equals(symbol)) {
            bitSet.and(bitSetNames);
        }
        if (SYMBOL.XOR.equals(symbol)) {
            bitSet.xor(bitSetNames);
        }
    }


    /**
     * 返回“逻辑大小”=最高位的索引加1。如果没有任何设置位，则返回零。
     *
     * @param key 键名
     * @return 返回“逻辑大小”=最高位的索引加1
     */
    public long bitpos(String key) {
        return redissonClient.getBitSet(key).length();
    }

    /**
     * 如果位设置为1则返回true，反之返回false。
     *
     * @param key      键名
     * @param bitIndex 位的索引
     * @return 如果位设置为1则返回true，反之返回false。
     */
    public boolean getbit(String key, long bitIndex) {
        RBitSet bitSet = redissonClient.getBitSet(key);
        return bitSet.get(bitIndex);
    }

    /**
     * 设置指定索引位置为1或0
     *
     * @param key      键名
     * @param bitIndex 位的索引
     * @param value    true = 1, false = 0
     */
    public void setbit(String key, long bitIndex, boolean value) {
        RBitSet bitSet = redissonClient.getBitSet(key);
        bitSet.set(bitIndex, value);
    }

    /**
     * 返回指定偏移量处的长整数
     *
     * @param key    键名
     * @param offset 偏移量
     * @return 长整数
     */
    public long bitfield(String key, long offset) {
        RBitSet bitSet = redissonClient.getBitSet(key);
        return bitSet.getLong(offset);
    }
    //----- bitmap

    //----- generic

    public boolean del(String key) {
        return redissonClient.getBucket(key).delete();
    }

    public boolean exists(String key) {
        return redissonClient.getBucket(key).isExists();
    }

    public boolean expire(String key, long timeToLive, TimeUnit timeUnit) {
        return redissonClient.getBucket(key).expire(timeToLive, timeUnit);
    }

    public long ttl(String key) {
        return redissonClient.getBucket(key).remainTimeToLive();
    }

    //----- generic
}
